﻿using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using ProgressTracker.Data.Abstract;
using ProgressTracker.Domain.Messages;
using ProgressTracker.Domain.Services;
using ProgressTracker.Domain.Translation;
using System;

namespace ProgressTracker.Tests.Domain.Services
{
    [TestClass]
    public class DayServiceTests
    {
        private Mock<IDayRepository> _dayRepo;
        private Mock<ITaskRepository> _taskRepo;
        private Mock<IDayMapper> _dayMapper;
        private Mock<ProgressTracker.Data.Entities.Day> _testDay;

        private DateTime _testDateTime;
        private const string _testUser = "User@user.com";

        private UpdateTaskCompletenessRequest _testUpdateCompleteRequest;
        private UpdateDayRequest _testUpdateDayRequest;

        [TestInitialize]
        public void Initialize()
        {
            _dayMapper = new Mock<IDayMapper>();
            _dayRepo = new Mock<IDayRepository>();
            _taskRepo = new Mock<ITaskRepository>();

            _testDay = new Mock<ProgressTracker.Data.Entities.Day>();
            _testDay.Setup(x => x.UpdateTaskCompleteness(It.IsAny<ProgressTracker.Data.Entities.Task>(), It.IsAny<bool>()));

            _testDateTime = new DateTime(2001, 1, 19);
            _testUpdateCompleteRequest = new UpdateTaskCompletenessRequest
            {
                Date = _testDateTime,
                IsComplete = true,
                TaskId = 1,
                UserEmail = _testUser
            };

            _testUpdateDayRequest = new UpdateDayRequest
            {
                Date = _testDateTime,
                Notes = "Test Notes",
                UserEmail = _testUser
            };

            _dayMapper.Setup(x => x.Create(It.IsAny<ProgressTracker.Data.Entities.Day>()))
                .Returns(new ProgressTracker.Domain.Day());
        }

        [TestMethod]
        public void GetDay_Gets_Correct_Day_From_Repository()
        {
            _dayRepo.Setup(x => x.GetDay(_testDateTime, _testUser))
                .Returns(new ProgressTracker.Data.Entities.Day()).Verifiable();

            var dayService = new DayService(_dayRepo.Object, _taskRepo.Object, _dayMapper.Object);
            dayService.GetDay(_testDateTime, _testUser);

            _dayRepo.Verify();
        }

        [TestMethod]
        public void GetDay_Creates_New_Day_If_Day_Does_Not_Exist()
        {
            ProgressTracker.Data.Entities.Day day = null;

            _dayRepo.Setup(x => x.GetDay(_testDateTime, _testUser))
                .Returns(day);

            _dayRepo.Setup(x => x.CreateNewDay(_testDateTime, _testUser))
                .Returns(new ProgressTracker.Data.Entities.Day()).Verifiable();

            var dayService = new DayService(_dayRepo.Object, _taskRepo.Object, _dayMapper.Object);
            dayService.GetDay(_testDateTime, _testUser);

            _dayRepo.Verify();
        }

        [TestMethod]
        public void UpdateTaskCompleteness_Gets_Correct_Day()
        {
            _dayRepo.Setup(x => x.GetDay(_testUpdateCompleteRequest.Date, _testUpdateCompleteRequest.UserEmail))
                .Returns(_testDay.Object).Verifiable();

            _taskRepo.Setup(x => x.GetTaskById(It.IsAny<int>()))
                .Returns(new ProgressTracker.Data.Entities.Task());

            var dayService = new DayService(_dayRepo.Object, _taskRepo.Object, _dayMapper.Object);
            dayService.UpdateTaskCompleteness(_testUpdateCompleteRequest);

            _dayRepo.Verify();
        }

        [TestMethod]
        public void UpdateTaskCompleteness_Gets_Correct_Task()
        {
            _dayRepo.Setup(x => x.GetDay(It.IsAny<DateTime>(), It.IsAny<string>()))
                .Returns(_testDay.Object);

            _taskRepo.Setup(x => x.GetTaskById(_testUpdateCompleteRequest.TaskId))
                .Returns(new ProgressTracker.Data.Entities.Task()).Verifiable();

            var dayService = new DayService(_dayRepo.Object, _taskRepo.Object, _dayMapper.Object);
            dayService.UpdateTaskCompleteness(_testUpdateCompleteRequest);

            _taskRepo.Verify();
        }

        [TestMethod]
        public void UpdateTaskCompleteness_Updates_Task_For_Day()
        {
            _testDay.Setup(x => x.UpdateTaskCompleteness(It.IsAny<ProgressTracker.Data.Entities.Task>(), _testUpdateCompleteRequest.IsComplete))
                .Verifiable();

            _dayRepo.Setup(x => x.GetDay(It.IsAny<DateTime>(), It.IsAny<string>()))
                .Returns(_testDay.Object);

            _taskRepo.Setup(x => x.GetTaskById(It.IsAny<int>()))
                .Returns(new ProgressTracker.Data.Entities.Task());

            var dayService = new DayService(_dayRepo.Object, _taskRepo.Object, _dayMapper.Object);
            dayService.UpdateTaskCompleteness(_testUpdateCompleteRequest);

            _testDay.Verify();
        }

        [TestMethod]
        public void UpdateTaskCompleteness_Saves_Changes()
        {
            _dayRepo.Setup(x => x.GetDay(It.IsAny<DateTime>(), It.IsAny<string>()))
                .Returns(_testDay.Object);

            _dayRepo.Setup(x => x.Save(It.IsAny<ProgressTracker.Data.Entities.Day>()))
                .Verifiable();

            _taskRepo.Setup(x => x.GetTaskById(It.IsAny<int>()))
                .Returns(new ProgressTracker.Data.Entities.Task());

            var dayService = new DayService(_dayRepo.Object, _taskRepo.Object, _dayMapper.Object);
            dayService.UpdateTaskCompleteness(_testUpdateCompleteRequest);

            _dayRepo.Verify();
        }

        [TestMethod]
        public void UpdateDay_Retrieves_Correct_Day()
        {
            _dayRepo.Setup(x => x.GetDay(_testUpdateCompleteRequest.Date, _testUpdateCompleteRequest.UserEmail))
                .Returns(_testDay.Object).Verifiable();

            var dayService = new DayService(_dayRepo.Object, _taskRepo.Object, _dayMapper.Object);
            dayService.UpdateDay(_testUpdateDayRequest);

            _dayRepo.Verify();
        }

        [TestMethod]
        public void UpdateDay_Saves_Changes()
        {
            _dayRepo.Setup(x => x.GetDay(_testUpdateCompleteRequest.Date, _testUpdateCompleteRequest.UserEmail))
                .Returns(new ProgressTracker.Data.Entities.Day());

            _dayRepo.Setup(x => x.Save(It.Is<ProgressTracker.Data.Entities.Day>(
                d => d.Notes == _testUpdateDayRequest.Notes))).Verifiable();

            var dayService = new DayService(_dayRepo.Object, _taskRepo.Object, _dayMapper.Object);
            dayService.UpdateDay(_testUpdateDayRequest);

            _dayRepo.Verify();
        }
    }
}
